package com.apes.scm.voucher.service.cost;

import com.apes.framework.api.SpringManager;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.account.model.InventoryVo;
import com.apes.scm.account.account.repository.InventoryBatchRepository;
import com.apes.scm.account.method.Batch;
import com.apes.scm.account.method.BatchMethod;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.model.DeptEnum;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherInventoryRepository;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.BusinessType;
import lombok.Data;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;


/**
 * 功能：销售订单成本核算
 *
 * @author xul
 * @create 2018-10-18 9:26
 */
@Component
public class SaleOrderCostMethod extends AbstractCostMethod {

    public SaleOrderCostMethod() {
        this.BusinessTypes = new String[]{BusinessType.SALE_ORDER, BusinessType.CUSTOMER_CONSUME_ORDER_VOUCHER};
    }

    /**
     * 成本核算/价值评估
     *
     * @param voucher
     */
    @Override
    public void costing(Voucher voucher) {
        if (voucher.getInventoryMode().equals(InventoryMode.IM_CONSIGN)) return;
        Collection<VoucherInventory> voucherInventories = costing(voucher, this::costing, this::createInventoryVoExtend);
        supplierConsume(voucher, voucherInventories); /**寄售库存，需要做供应商寄售消耗**/
    }

    @Override
    protected VoucherInventory createVoucherInventory(VoucherItem voucherItem, InventoryBatch inventoryBatch, double changeQty) {
        VoucherInventory voucherInventory = super.createVoucherInventory(voucherItem, inventoryBatch, changeQty);
        if (inventoryBatch.getCooperation().getAccountingMethod().equals(AccountingMethod.AM_REAL)) {
            //todo 重新取供应商价格
            throw new RuntimeException("请重取供应商价格！");
            // 重新计算成本单价
//            double taxCostPrice = inventoryBatch.getUom().computePrice(inventoryBatch.getTaxCostPrice(), voucherItem.getUom());
//            voucherInventory.setTaxCostPrice(taxCostPrice);
        }
        return voucherInventory;
    }

    /**
     * 功能：销售价值评估
     */
    private void costing(CostContext context) {
        Set<InventoryVo> inventoryVos = new HashSet<>();
        InventoryVo inventoryVo = context.getInventoryVo();

        new BatchMethod<Batch>() {

            @Override
            public Iterable wrap() {
                List<InventoryBatch> inventories;

                Dept dept = inventoryVo.getDept();
                if (dept == null) {
                    throw new RuntimeException("业务凭证销售部门为空！");
                }
                if (!(dept.existDeptType(DeptEnum.RETAIL) || dept.existDeptType(DeptEnum.SALE))) {
                    throw new RuntimeException("业务凭证部门为非销售部门！");
                }
                if (!dept.existDeptType(DeptEnum.PURCHASE)) {
                    throw new RuntimeException("业务凭证部门为非采购部门！");
                }

                /**指定批次**/
                if (isSureInventories(inventoryVo)) {
                    inventories = inventoryVo.getInInventories();

                    if (inventories.stream().filter(inventoryBatch -> !dept.equals(inventoryBatch.getDept())).count() > 0) {
                        throw new RuntimeException("批次采购部门与采购部门不一致，请重新确认！");
                    }

                    if (inventories.stream().filter(inventoryBatch -> !inventoryVo.getCompany().equals(inventoryBatch.getCompany())).count() > 0) {
                        throw new RuntimeException("批次公司与单据公司不一致，请重新确认！");
                    }
                    List<Long> batchNumbers = inventories.stream().map(inventoryBatch -> inventoryBatch.getBatchNumber()).collect(Collectors.toList());
                    inventories = inventoryService.findAllByBatchNumber(batchNumbers, inventory -> inventory.getFrostQty());
                    return inventoryService.wrapBatch(inventories, inventoryVo.getUom(), inventory -> inventory.getFrostQty());
                } else { /**未指定批次**/
                    if (inventoryVo.getSupplier() == null) {
                        inventories = inventoryService.findInventoryBatchForSaleOrder(inventoryVo.getCompany(), dept, inventoryVo.getProduct());
                    } else {
                        inventories = inventoryService.findInventoryBatchForSaleOrder(inventoryVo.getCompany(), dept, inventoryVo.getSupplier(), inventoryVo.getProduct());
                    }
                    return inventoryService.wrapBatch(inventories, inventoryVo.getUom(), inventory -> inventory.getBalanceQty() - inventory.getFrostQty());
                }

            }

            @Override
            public void succeed(Batch batch, double increment) {
                InventoryBatch inventoryBatch = (InventoryBatch) batch.getEntity();
                double qty = inventoryVo.getUom().computeQty(increment, inventoryBatch.getUom(), true);
                if (isSureInventories(inventoryVo)) {
                    inventoryService.changeFrostQty(inventoryBatch, inventoryBatch.getUom(), -1 * qty, inventoryBatch.getCompany(),
                            context.getVoucherItem().getVoucher().getInnerBusinessType(),
                            context.getVoucherItem().getVoucher().getInnerBusinessKey(),
                            String.valueOf(context.getVoucherItem().getId()),
                            "销售价值评估批次减少冻结数量");
                }
                inventoryBatch.changeBalanceQty(-1 * qty);
                inventoryBatch.changeSalesQty(qty);
                inventoryService.save(inventoryBatch);
                InventoryVo vo = new InventoryVo();
                vo.setOutInventoryBatch(inventoryBatch);
                vo.setQty(increment);
                inventoryVos.add(vo);
            }

            @Override
            public void error(double surplus) {
                throw new RuntimeException("商品：["+inventoryVo.getProduct().getId()+"]" + inventoryVo.getProduct().getName() + " 价值库存数量少 " + surplus + inventoryVo.getUom().getName() + "！");
            }
        }.execute(inventoryVo.getQty());

        context.setInventoryVos(inventoryVos);
    }

    private void createInventoryVoExtend(VoucherItem voucherItem, InventoryVo vo) {
        vo.setDept(voucherItem.getVoucher().getDept());
        /**指定供应商：第三方销售（使用场景）**/
        if (voucherItem.getSupplier() != null) vo.setSupplier(voucherItem.getSupplier());
    }


    @Autowired
    private EntityManager em;

    @Autowired
    private VoucherRepository voucherRepository;

    @Autowired
    private InventoryBatchRepository inventoryBatchRepository;

    @Autowired
    private VoucherInventoryRepository voucherInventoryRepository;

    /**
     * 重新结转成本（调帐专用）（慎用）
     **/
    public Map recosting(SimpleRequest request) {
        // 字段说明: voucher_id 销售凭证编码， voucher_item_id 销售凭证对应的明细， batch_id 新的批次号
        String sql = "select voucher_id, voucher_item_id, batch_id\n" +
                "  from v_retatil_order_item_costing\n" +
                " where batch_id is not null\n" +
                "   and voucher_id is not null\n" +
                "   and voucher_item_id is not null";
        List<OrderItem> orderItems = new ArrayList<>();
        em.createNativeQuery(sql).unwrap(NativeQueryImpl.class)
                .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
                .getResultStream()
                .forEach(m -> {
                    String voucherId = (String) ((Map) m).get("VOUCHER_ID");
                    String voucherItemId = String.valueOf((BigDecimal) ((Map) m).get("VOUCHER_ITEM_ID"));
                    long batchId = ((BigDecimal) ((Map) m).get("BATCH_ID")).longValue();
                    OrderItem orderItem = new OrderItem(voucherId, voucherItemId, batchId);
                    orderItems.add(orderItem);
                });
        orderItems.stream().collect(groupingBy(OrderItem::getVoucherId)).forEach((voucherId, items) -> {
            Voucher voucher = voucherRepository.findOne(voucherId);
            if (voucher.getInventoryMode().equals(InventoryMode.IM_CONSIGN)) return;

            List<VoucherInventory> voucherInventories = new ArrayList<>();
            voucher.getVoucherItems().forEach((voucherItem) -> {
                OrderItem orderItem = items.stream().filter(item -> item.getVoucherItemId().equals(String.valueOf(voucherItem.getId()))).findFirst().orElse(null);
                if (orderItem == null) return;
                InventoryBatch inventoryBatch = inventoryBatchRepository.findOne(orderItem.batchId);
                if (inventoryBatch == null) throw new RuntimeException("批次：" + orderItem.batchId + " 不存在！");

                /**删除原来的价值评估明细**/
                List<VoucherInventory> oldInventories = valuationService.findByVoucherItem(voucherItem);
                oldInventories.stream().forEach(vi -> {
                    InventoryBatch ib = vi.getInventoryBatch();
                    double qty = vi.getUom().computeQty(vi.getProductQty(), inventoryBatch.getUom(), true);
                    ib.changeBalanceQty(qty);
                    ib.changeSalesQty(-1 * qty);
                    inventoryService.save(ib);
                });
                voucherInventoryRepository.deleteAll(oldInventories);

                voucherItem.setInventoryBatch(inventoryBatch);
                InventoryVo vo = createInventoryVo(voucherItem, this::createInventoryVoExtend);
                CostContext context = new CostContext(voucherItem, vo);
                /**价值评估**/
                costing(context);
                context.getInventoryVos().stream().forEach((inventoryVo) -> {
                    InventoryBatch outInventoryBatch = inventoryVo.getOutInventoryBatch();
                    double qty = voucherItem.sign() * inventoryVo.getQty();
                    VoucherInventory voucherInventory = createVoucherInventory(voucherItem, outInventoryBatch, qty);
                    voucherInventories.add(voucherInventory);
                });
            });
            if (!voucherInventories.isEmpty()) {
                SpringManager.getBean(VoucherRepository.class).saveAndFlush(voucher);
                List<VoucherInventory> inventories = valuationService.save(voucherInventories);
                supplierConsume(voucher, inventories); /**寄售库存，需要做供应商寄售消耗**/
            }
        });

        return new HashMap();
    }

    @Data
    class OrderItem {
        private String voucherId;
        private String voucherItemId;
        private long batchId;

        public OrderItem(String voucherId, String voucherItemId, long batchId) {
            this.voucherId = voucherId;
            this.voucherItemId = voucherItemId;
            this.batchId = batchId;
        }
    }
}
